﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using System.Threading;
using Microsoft.SharePoint;
using System.Diagnostics;
using System.Net;
using System.Text.RegularExpressions;

namespace WarmMeUp
{
    public class WarmMeUpJob : SPJobDefinition
    {
        #region props
        public override string Description
        {
            get
            {
                return Constants.JobDescription;
            }
        }
        public override string DisplayName
        {
            get
            {
                return Constants.JobName;
            }
        }
        public override bool CanRenameOnRestore
        {
            get
            {
                return true;
            }
        }
        public override ulong DiskSizeRequired
        {
            get
            {
                return 0L;
            }
        }
        #endregion

        #region ctors
        public WarmMeUpJob()
            : base()
        {

        }
        public WarmMeUpJob(string name, SPService service, SPServer server, SPJobLockType lockType)
            : base(name, service, server, lockType)
        {

        }
        public WarmMeUpJob(string name, SPWebApplication wa, SPServer server, SPJobLockType lockType)
            : base(name, wa, server, lockType)
        {

        }
        #endregion
        #region methods
        public override void Execute(Guid targetInstanceId)
        {
            SPWebService webService = SPWebService.ContentService;
            AwakeWebApplication(webService, this.WebApplication.Id);


            // Now, we have to manage additional URLS
            if (this.WebApplication.Properties.ContainsKey(Constants.AdditionalUrls))
            {
                // Getting additional URL for custom property 
                string Urls = this.WebApplication.Properties[Constants.AdditionalUrls] as string;
                // if some URL are define
                if (Urls.Trim().Length > 0)
                {
                    // Browing each URL
                    foreach (string url in Urls.Split('\t'))
                    {
                        // We check the URL is not empty
                        if (url.Trim().Length > 0)
                            AwakeURL(url);
                    }
                }
            }
        }

        /// <summary>
        /// Method in charge to awake a webApplication
        /// </summary>
        /// <param name="id"></param>
        void AwakeWebApplication(SPWebService webService, Guid webApplicationId)
        {
            // Creating thread for async work
            Thread webApplicationThread = new Thread(delegate(object param)
            {
                List<SPSite> sitesToDispose = new List<SPSite>();

                IEnumerable<SPServer> servers = SPFarm.Local.Servers.Where(x => x.Role != SPServerRole.Invalid && x.ServiceInstances.OfType<SPWebServiceInstance>().Count() > 0);
                Regex reg = new Regex("(?<protocol>http[s]?://)(?<fqdn>[a-zA-Z0-9-_.]*)(?<port>[:]?[0-9]*)?(?<rest>.*)");

                SPWebApplication webApp = webService.WebApplications[(Guid)param];
                List<string> baseurls = new List<string>(webApp.AlternateUrls.Where(x => webApp.IisSettings[x.UrlZone].AuthenticationMode == System.Web.Configuration.AuthenticationMode.Windows).Select(y => y.IncomingUrl));

                foreach (SPSite site in webApp.Sites)
                {
                    List<SPWeb> webToDispose = new List<SPWeb>();

                    // Browsing each web of the site
                    foreach (SPWeb web in site.AllWebs)
                    {
                        if (baseurls.Count > 0)
                            foreach (string baseurl in baseurls)
                            {
                                if (baseurl.Contains('.'))//it is a fqdn so we don't have to crawl all servers
                                    AwakeURL(AddSlash(baseurl, web.ServerRelativeUrl));
                                else
                                    foreach (SPServer server in servers)
                                    {
                                        string fqdn = reg.Match(baseurl).Groups["fqdn"].Value;
                                        AwakeURL(AddSlash(baseurl.Replace(fqdn, server.Name), web.ServerRelativeUrl));
                                    }
                                AwakeURL(web.Url);
                                webToDispose.Add(web);
                            }
                        else
                            AwakeURL(web.Url);
                    }
                    webToDispose.ForEach(x => x.Dispose());
                    sitesToDispose.Add(site);
                }

                sitesToDispose.ForEach(x => x.Dispose());
            });

            // Start the thread
            webApplicationThread.Start(webApplicationId);
        }

        private static string AddSlash(string beginurl, string endurl)
        {
            string slash = string.Empty;
            if (!beginurl.EndsWith("/") && !endurl.StartsWith("/"))
                slash = "/";
            return beginurl + slash + endurl;
        }

        /// <summary>
        /// Method in charge to awake an URL
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AwakeURL(string url)
        {
            try
            {
                // Thread to awake URL async
                Thread awakeUrlThread = new Thread(delegate(object param)
                {
                    // Request to wake up
                    Uri request = new Uri((string)param);

                    Debug.WriteLine(string.Format("Request on {0}", request.ToString()));

                    // Creating a WebClient to execute the request
                    WebClient webClient = new WebClient();
                    webClient.Credentials = System.Net.CredentialCache.DefaultCredentials;

                    // Executing the request
                    webClient.DownloadStringAsync(request);
                });

                // Starting the thread
                awakeUrlThread.Start(url);
            }
            catch (Exception)
            {
                // Nothing to do
            }

        }
        #endregion
    }
}
